(function($) {	
	KOR.inlineZoom = function(selector, mixin) {
		return KOR.InlineZoom.getInstanceOf(selector, mixin);
	};
	
	KOR.InlineZoom = KOR.createClass(KOR.LazyObject, {
		NAME: 'inlineZoom',
		
		initAction: function (node, ev, data) {
			return new InlineZoom({
				name: this.NAME,
				node: $(node),
				panOn: this.panOn || 0,
				zoomOn: this.zoomOn || 0,
				image: this.image || null,
				zoomDimensions: this.zoomDimensions || {
					height:500, width:500
				},
				imgClass: this.imgClass || 'kor-zoom-default'
			});
		}
	});
	
	var InlineZoom = KOR.createClass({
		
		/* Constants */
		ZOOMTRIGGER_UNKNOWN: 0,
		ZOOMTRIGGER_CLICK: 1,
		ZOOMTRIGGER_HOVER: 2,
		
		PANTRIGGER_UNKNOWN: 0,
		PANTRIGGER_DRAGDROP: 1,
		PANTRIGGER_MOUSEMOVE: 2,
		
		/* Configuration items */
		panOn: this.PANTRIGGER_UNKNOWN,
		zoomOn: this.ZOOMTRIGGER_UNKNOWN,
		image: null,
		zoomDimensions: {height:500, width:500},
		imgClass: 'kor-zoom-default',
		
		/* Private variables */
		eventsRegistered: false,
		imgElem: null,
		imgSelector: null,
		panning: false,
		zoomed: false,
		zoomAnimating: false,
		originalImgSrc: null,
		originalImgCss: null,
		panStart: {x:0, y:0, imgX:0, imgY:0},
		frameDim: {width:200, height:200, offsetX:0, offsetY:0},
		normalClick: true,
		
		init: function(mixin) {
			$.extend(this, mixin);
			this.modalSelector = '.' + this.modalClass;
			this.imgSelector = '.' + this.imgClass;
			this.imgElem = $(this.imgSelector, this.node);
			if (!this.panOn) this.determinePanTrigger();
			if (!this.zoomOn) this.determineZoomTrigger();
			this.node.css({
				width: this.node.width() + 'px',
				height: this.node.height() + 'px',
				position: 'relative',
				overflow: 'hidden'
			});
			var offset = this.node.offset();
			this.frameDim = {
				width: this.node.width(),
				height: this.node.height(),
				offsetX: offset.left,
				offsetY: offset.top
			};
			if (!this.originalImgCss) {
				this.originalImgCss = {
					width: this.imgElem.width() + 'px',
					height: this.imgElem.height() + 'px'
				};
			}
			this.registerEvents();
		},
		
		configure: function(mixin) {
			this.init(mixin);
		},
		
		determinePanTrigger: function() {
			var attr = this.node.attr('data-zoom-pan');
			switch (attr) {
				case 'dragdrop':
					this.panOn = this.PANTRIGGER_DRAGDROP; break;
				case 'mousemotion':
					this.panOn = this.PANTRIGGER_MOUSEMOVE; break;
				default:
					this.panOn = this.PANTRIGGER_UNKNOWN;
			}
		},
		
		determineZoomTrigger: function() {
			var attr = this.node.attr('data-zoom-trigger');
			switch (attr) {
				case 'click':
					this.zoomOn = this.ZOOMTRIGGER_CLICK; break;
				case 'hover':
					this.zoomOn = this.ZOOMTRIGGER_HOVER; break;
				default:
					this.zoomOn = this.ZOOMTRIGGER_UNKNOWN;
			}
		},
		
		registerEvents: function() {
			if (!this.eventsRegistered) {
				var cancel = function () { return false; },
					root = $($.browser.msie ? document : window);
				this.imgElem.bind('dragstart', cancel);
				this.imgElem.bind('dragend', cancel);
				this.imgElem.bind('drag', cancel);
				this.imgElem.bind('dblclick', $.proxy(this, 'zoomOutHandler'));
				this.imgElem.bind('click', $.proxy(this, 'zoomInHandler'));
				this.imgElem.bind('mousedown', $.proxy(this, 'panStartHandler'));
				this.imgElem.bind('mouseenter', $.proxy(this, 'hoverStartHandler'));
				this.imgElem.bind('mouseleave', $.proxy(this, 'hoverEndHandler'));
				this.node.bind('mousemove', $.proxy(this, 'hoverPanHandler'));
				root.delegate('html', 'mousemove', $.proxy(this, 'dragPanHandler'));
				root.delegate('html', 'mouseup', $.proxy(this, 'panEndHandler'));
				root.delegate('html', 'mouseleave', $.proxy(this, 'panEndHandler'));	
				this.eventsRegistered = true;
			}
		},
		
		panStartHandler: function(ev) {
			if (this.zoomed) {
				this.panning = true;
				this.panStart = {
					x: ev.pageX,
					y: ev.pageY,
					imgX: this.frameDim.width / 2 - parseInt(this.imgElem.css('left')),
					imgY: this.frameDim.height / 2 - parseInt(this.imgElem.css('top'))
				};
				this.normalClick = true;
			}
		},
		
		hoverStartHandler: function(ev) {
			if (this.zoomOn == this.ZOOMTRIGGER_HOVER) this.zoomInHandler(ev, true);
			if (this.panOn == this.PANTRIGGER_MOUSEMOVE) this.panStartHandler(ev);
		},
		
		panEndHandler: function() {
			if (this.panOn == this.PANTRIGGER_DRAGDROP) this.panning = false;
		},
		
		hoverEndHandler: function() {
			if (this.panOn == this.PANTRIGGER_MOUSEMOVE) this.panning = false;
			if (this.zoomOn == this.ZOOMTRIGGER_HOVER) this.zoomOutHandler(null, true);
		},
		
		dragPanHandler: function(ev) {
			if (this.panning && this.panOn == this.PANTRIGGER_DRAGDROP) {
				var dX = ev.pageX - this.panStart.x,
					dY = ev.pageY - this.panStart.y;
				this.panTo(this.panStart.imgX - dX, this.panStart.imgY - dY);
				this.normalClick = false;
			}
		},
		
		hoverPanHandler: function(ev) {
			if (this.panning && this.panOn == this.PANTRIGGER_MOUSEMOVE) {
				var rX = (ev.pageX - this.frameDim.offsetX) / this.frameDim.width,
					rY = (ev.pageY - this.frameDim.offsetY) / this.frameDim.height,
					tX = Math.floor(this.zoomDimensions.width * rX),
					tY = Math.floor(this.zoomDimensions.height * rY);
				this.panTo(tX, tY);
			}
		},
		
		zoomInHandler: function(ev, skipAnimation) {
			var x = ev.layerX || ev.offsetX,
				y = ev.layerY || ev.offsetY;
			if (!this.zoomed) this.zoomTo(x, y, skipAnimation);
			else if (this.normalClick && this.panOn == this.PANTRIGGER_DRAGDROP) {
				this.panTo(x, y);
			}
		},
		
		zoomOutHandler: function(ev, skipAnimation) {
			if (this.zoomed && !this.zoomAnimating) {
				var self = this;
				this.imgElem.attr('src', this.originalImgSrc).stop();
				if (skipAnimation) {
					this.imgElem.css($.extend(this.originalImgCss, {top:0, left:0}));
					this.zoomed = false;
				}
				else {
					this.zoomAnimating = true;
					this.imgElem.animate($.extend(this.originalImgCss, {top:0, left:0}), function() {
						self.zoomed = false;
						self.zoomAnimating = false;
					});
				}
				this.panning = false;
			}
		},
		
		panTo: function(x, y) {
			if (this.zoomed && !this.zoomAnimating) {
				var toCss = {
					top: Math.min(this.frameDim.width / 2 - y, 0),
					left: Math.min(this.frameDim.height / 2 - x, 0)
				}
				toCss.top = Math.max(toCss.top, this.frameDim.height - this.zoomDimensions.height) + 'px';
				toCss.left = Math.max(toCss.left, this.frameDim.width - this.zoomDimensions.width) + 'px';
				if (this.panning) this.imgElem.css(toCss);
				else this.imgElem.stop().animate(toCss);
			}
		},
		
		zoomTo: function(x, y, skipAnimation) {
			if (!this.zoomed && !this.zoomAnimating) {
				var imgWidth = this.imgElem.width(),
					imgHeight = this.imgElem.height(),
					targetX = Math.floor(this.zoomDimensions.width / imgWidth * x),
					targetY = Math.floor(this.zoomDimensions.height / imgHeight * y),
					toCss = {
						top: Math.min(this.frameDim.height / 2 - targetY, 0),
						left: Math.min(this.frameDim.width / 2 - targetX, 0),
						width: this.zoomDimensions.width + 'px',
						height: this.zoomDimensions.height + 'px'
					},
					self = this;
				this.originalImgSrc = this.imgElem.attr('src');
				toCss.top = Math.max(toCss.top, this.frameDim.height - this.zoomDimensions.height) + 'px';
				toCss.left = Math.max(toCss.left, this.frameDim.width - this.zoomDimensions.width) + 'px';
				this.imgElem.css(this.originalImgCss).attr('src', this.image).stop();
				if (skipAnimation) {
					this.imgElem.css(toCss);
					this.zoomed = true;
				}
				else {
					this.zoomAnimating = true;
					this.imgElem.animate(toCss, function() {
						self.zoomed = true;
						self.zoomAnimating = false;
					});
				}
			}
		},
		
		zoomToggle: function() {
			if (this.zoomed) this.zoomOutHandler();
			else {
				this.zoomTo(this.imgElem.width() / 2,
					this.imgElem.height() / 2);
			}
		}
	});

})(jQuery);